package com.zimo.download;

import com.zimo.download.exception.DownloadException;
import com.zimo.download.info.PointerOffset;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

/**
 * 生成断点文件，通过断点文件可以续传
 * 可能给URL和path预分配的空间有些大，但是保不准有些丧心病狂的URL或者path存在呢
 * 断点文件结构
 * [0-1024)：url
 * [1024-2048)：path 文件保存路径
 *  2068之后每16字节都是一对start，end，他们每人占用8字节
 */
public class DownloadCacheImpl implements DownloadCache {
    private String url;//1024个字节，从0开始
    private String path;//1024个字节，从1024开始,文件保存位置  D://tim/QQ.exe
    private int threadID;//线程id，为了计算偏移量

    //获取logger对象： 传入当前类的class，这样代表这个类输出的日志信息
    private Logger logger = Logger.getLogger(DownloadCacheImpl.class);

    private long offset;    //偏移量，从2048+16开始每16个字节就是一对start，end
    private RandomAccessFile raf=null;


    public DownloadCacheImpl(String url, String path,  String threadID) throws FileNotFoundException {
        this.url = url;

        //获取当前线程的偏移量，与线程ID
        String sp []  = threadID.split(" --- ");//切割线程名称，来获取线程的ID  QQ --- 1
        String s = sp[sp.length - 1];//获取ID
        this.threadID =Integer.valueOf(s);//将ID转化为int
        this.offset  = (this.threadID*16)+2048;//初始化偏移量,为这个ID的线程设置偏移量


        this.path = path+".tim";
        this.raf = new RandomAccessFile(this.path,"rw");
        logger.debug("缓存/断点文件已被创建");
    }
    public DownloadCacheImpl(String path) throws FileNotFoundException {
        this.path=path;
        this.raf = new RandomAccessFile(this.path,"r");
    }

    @Override
    public void setBreakpoint(long start,long end,String filePath) throws DownloadException {
        try {
            if (start < 0 ||end <=0 ) throw new DownloadException("断点写入异常,start指针不可小于0,end指针不可为小于等于0");

            writePath(filePath);
            writeURL(url);
            writeStart(start);
            writeEnd(end);
        } catch (Exception e) {
            logger.error("断点流写入异常，可能是流为null或者文件资源异常流无法操作,具体异常: ",e);
            throw new DownloadException("断点流写入异常，可能是流为null或者文件资源异常流无法操作");
        }
    }

    @Override
    public boolean continuation(String path) {
        return false;
    }

    //断点文件的位置
    public List<PointerOffset> getPointerOffsets() throws IOException {
        RandomAccessFile r = new RandomAccessFile(this.path,"r");
        long length = new File(this.path).length();//获取文件大小
        List<PointerOffset> pointerOffsets = new ArrayList<>();

        //获取所有的指针
        final int OFFSET = 2048;
        int i = 0;

       while (((16*i)+OFFSET) < length){
           //获取start指针
           r.seek((16*i)+OFFSET);
           long start = r.readLong();

           //获取end指针
           r.seek((16*i+OFFSET)+8);
           long end = r.readLong();

           //将指针放入集合
           pointerOffsets.add(new PointerOffset(start,end));
           i++;
       }

        logger.info("获取断点文件" +
                "\n断点文件位置： "+this.path+
                "\n断点文件大小： "+new File(this.path).length()+"kb"+
                "\n断点指针: "+log(pointerOffsets));
        return pointerOffsets;
    }

    String log(List<PointerOffset> pointerOffsets){
        String s ="\n";
        int i = 1;
        for (PointerOffset pointerOffset : pointerOffsets) {
            s=s+i+"<<<"+pointerOffset+"\n";
            i++;
        }
        return s;
    }

    /**
     * 清理缓存文件，只会清理和下载文件在一起的缓存文件
     * @param path
     * @return
     */
    public static boolean clear(String path,String name){
        String lastName = name.substring(name.lastIndexOf(".") + 1);
        if ("tim".equals(lastName)){
            return new File(path+"//"+name).delete();
        }else {
            name = name+".tim";
            return new File(path+"//"+name).delete();
        }
    }


    /**
     * 写入
     */
    //url从0开始写入，最大为1024
    private void writeURL(String url) throws IOException {
        raf.seek(0);
        raf.write(new String(url.getBytes(),"utf-8").getBytes());
    }

    //path从0开始写入，最大为1024
    private void writePath(String path) throws IOException {
        raf.seek(1024);
        raf.write(new String(path.getBytes(),"utf-8").getBytes());
    }


    /*  start从offset开始写入，长度为8,
        offset是一个等比数组中的一项，计算方法: 线程ID*比值+2048，2048为path，url所在不可写入
        比值计算方法: 2*8     包含开始和结束的共用字节数,开始站8，结束站8
     */
    private void writeStart(long start) throws IOException {
        raf.seek(offset);
        raf.writeLong(start);
    }

    private void writeEnd(long end) throws IOException {
        raf.seek(offset+8);
        raf.writeLong(end);
    }


    /**
     * 读取缓存文件内的URL
     */
    public String readURL() throws IOException {
        raf.seek(0);
        byte b  []= new byte[1024];
        raf.read(b);
        return new String(b).trim();
    }

    /**
     * 读取 tim 文件内的文件保存路径
     * @return
     * @throws IOException
     */
    public String readPath() throws IOException {
        raf.seek(1024);
        byte b  []= new byte[1024];
        raf.read(b);
        return new String(b).trim();
    }

    /**
     * 获取的是当前偏移量的start
     */
    public long readStart() throws IOException {
        raf.seek(offset);
        return raf.readLong();
    }

    public long readEnd() throws IOException {
        raf.seek(offset+8);
        return raf.readLong();
    }

    public int readThreadCount() throws IOException {
        raf.seek(2048+4);
        return raf.readInt();
    }

    public void close(){
        try {
            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
